﻿using System;
using System.Collections.Generic;
using System.Data.Services.Client;
using System.Linq;
using nRoute.Components;
using nRoute.Components.Composition;
using nRoute.Mapping;
using nRoute.Samples.Netflix.Data;
using nRoute.Samples.Netflix.Data.Catalog;
using nRoute.Samples.Netflix.Views;
using nRoute.ViewModels;
using System.Collections.Specialized;
using System.Diagnostics;

namespace nRoute.Samples.Netflix.ViewModels
{
    [DefineNavigationContent("Content/Netflix/Titles/", typeof(TitlesListingView))]
    [MapViewModel(typeof(TitlesListingView))]    
    public class TitlesListingViewModel
        : NavigationViewModelBase
    {
        private const int PAGE_SIZE = 50;
        private const string MOVIE_QUERY_KEY = "MovieQuery";
        private const string MOVIE_TITLES_KEY = "MovieTitles";
        private const string PAGE_TITLE_KEY = "PageTitle";
        private const string LAST_PAGEINDEX_KEY = "LastPageIndex";
        private const string TOTAL_COUNT_KEY = "TotalTitlesCount";

        private DataServiceQuery<Title> _query;
        private DataServiceCollection<Title> _titles;
        private NetflixDataContext _context;

        [ResolveConstructor]
        public TitlesListingViewModel(NetflixDataContext context)
        {
            _context = context;
            SetupCommands();            
        }

#region Properties

        public IEnumerable<Title> Titles
        {
            get { return _titles != null ? _titles.Skip(PAGE_SIZE * (CurrentPageIndex - 1)).Take(PAGE_SIZE) : null; }
        }

        public bool IsLoading { get; private set; }

        public ActionCommand NextPageCommand { get; private set; }

        public ActionCommand PreviousPageCommand { get; private set; }

        public int CurrentPageIndex { get; private set; }

        public long? TotalTitlesCount { get; private set; }

#endregion

#region Overrides

        protected override void OnInitialize(ParametersCollection state)
        {
            base.Title = Convert.ToString(state[PAGE_TITLE_KEY]);

            // if there is last page index data, it means it will be restored from state
            if (!state.ContainsKey(LAST_PAGEINDEX_KEY))
            {
                _query = (DataServiceQuery<Title>)state[MOVIE_QUERY_KEY];
                SetupTitles(_query, 1);
            }
        }

        protected override void OnRestoreState(ParametersCollection state)
        {
            // if movie titles are already loaded we don't load again
            if (state == null) return;

            if (state.ContainsKey(MOVIE_TITLES_KEY))
            {
                _query = (DataServiceQuery<Title>)state[MOVIE_QUERY_KEY];
                _titles = (DataServiceCollection<Title>)state[MOVIE_TITLES_KEY];

                // if things are already loaded, use continuation
                if (_titles.Count != 0)
                {                    
                    CurrentPageIndex = Convert.ToInt32(state[LAST_PAGEINDEX_KEY]);
                    NotifyPropertyChanged(() => CurrentPageIndex);

                    TotalTitlesCount = (long?)state[TOTAL_COUNT_KEY];
                    NotifyPropertyChanged(() => TotalTitlesCount);

                    UpdatePageIndexedTitles();
                }
                else 
                {
                    SetupTitles(_query, 1);
                }
            }
            else
            {
                // inform no loaded data
            }
        }

        protected override ParametersCollection OnSaveState()
        {
            if (_titles != null)
            {
                return new ParametersCollection() 
                { 
                    new Parameter(MOVIE_QUERY_KEY, _query), 
                    new Parameter(MOVIE_TITLES_KEY, _titles), 
                    new Parameter(PAGE_TITLE_KEY, base.Title),
                    new Parameter(LAST_PAGEINDEX_KEY, CurrentPageIndex),
                    new Parameter(TOTAL_COUNT_KEY, TotalTitlesCount)
                };
            }
            else
            {
                return null;
            }
        }

#endregion

#region Helpers

        private void SetupCommands()
        {
            this.NextPageCommand = new ActionCommand(() =>
            {
                CurrentPageIndex += 1;
                NotifyPropertyChanged(() => CurrentPageIndex);
                UpdatePageIndexedTitles();
            });

            this.PreviousPageCommand = new ActionCommand(() =>
            {
                CurrentPageIndex -= 1;
                NotifyPropertyChanged(() => CurrentPageIndex);
                UpdatePageIndexedTitles();
            });
        }

        private void SetupTitles(DataServiceQuery<Title> query, int pageIndex)
        {
            if (_titles == null)
            {
                _titles = new DataServiceCollection<Title>();
                NotifyPropertyChanged(() => Titles);
            }

            CurrentPageIndex = pageIndex;
            NotifyPropertyChanged(() => CurrentPageIndex);

            IsLoading = true;
            NotifyPropertyChanged(() => IsLoading);

            // holds the result
            IAsyncResult _result = null;

            try
            {
                // we load everything at once
                var _query = (DataServiceQuery<Title>)(query.Skip(PAGE_SIZE * (CurrentPageIndex - 1)).Take(PAGE_SIZE));
                
                // execute async 
                _result = _query.BeginExecute((i) =>
                {
                    System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
                    {
                        var _response = (QueryOperationResponse<Title>)_query.EndExecute(_result);
                        TotalTitlesCount = _response.TotalCount;
                        NotifyPropertyChanged(() => TotalTitlesCount);

                        _titles.Load(_response);
                        NotifyPropertyChanged(() => Titles);

                        IsLoading = false;
                        NotifyPropertyChanged(() => IsLoading);

                        UpdatePageIndexedTitles();
                    }), null);
                }, null);
            }
            catch
            {
                // todo: handle properly
                Debugger.Break();
                if (_result != null && !_result.IsCompleted) _context.CancelRequest(_result);

                // reset
                _titles = null;
                NotifyPropertyChanged(() => Titles);
                SetupTitles(query, pageIndex);
            }
        }

        private void UpdatePageIndexedTitles()
        {
            if (CurrentPageIndex * PAGE_SIZE > _titles.Count)
            {
                if (TotalTitlesCount.GetValueOrDefault(0) == 0 || TotalTitlesCount.GetValueOrDefault(0) > _titles.Count)
                {
                    SetupTitles(_query, CurrentPageIndex);
                    return;
                }
                else
                {
                    NextPageCommand.IsActive = false;
                }
            }
            else
            {
                NextPageCommand.IsActive = true;
            }

            PreviousPageCommand.IsActive = (CurrentPageIndex != 1);
            NotifyPropertyChanged(() => Titles);
        }

#endregion

    }
}